#include <dspSimulator/instruction/set.h>

bool SET::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    if (isCompactInstruction) {
        return true;
    } else {
        return checkStandardCondition(dspMachine);
    }
}

void SET::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                            std::shared_ptr<llvm::IRBuilder<>> irBuilder) {
}

std::shared_ptr<Instruction> SET::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<SET> set;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (checkBits<6>(data, 2, 0b100010) && existStandardCondition(data)) {
            set = std::make_shared<SET>(dspInstInfo->getAddress(), baseCycle);
            set->creg_z = extractBits<4>(data, 28);
            set->dst = extractBits<5>(data, 23).to_ulong();
            set->src2 = extractBits<5>(data, 18).to_ulong();
            set->cst = extractBits<5>(data, 13).to_ulong();
            set->auxCst = extractBits<5>(data, 8).to_ulong();
            set->s = data[1];
            set->p = data[0];
            if (set->s == 0) {
                set->funcUnit = FuncUnit::S1;
            } else {
                set->funcUnit = FuncUnit::S2;
            }
            set->instFormat = InstFormat::Kind1;
        } else if (checkBits<10>(data, 2, 0b1110111000) && existStandardCondition(data)) {
            set = std::make_shared<SET>(dspInstInfo->getAddress(), baseCycle);
            set->creg_z = extractBits<4>(data, 28);
            set->dst = extractBits<5>(data, 23).to_ulong();
            set->src2 = extractBits<5>(data, 18).to_ulong();
            set->src1 = extractBits<5>(data, 13).to_ulong();
            set->x = data[12];
            set->s = data[1];
            set->p = data[0];
            if (set->s == 0) {
                set->funcUnit = FuncUnit::S1;
            } else {
                set->funcUnit = FuncUnit::S2;
            }
            set->instFormat = InstFormat::Kind2;
        }
    } else {
        auto data = vectorToBitSet<16>(dspInstInfo->getBits());
        if (data[10] == 0 && checkBits<2>(data, 5, 0b01) && checkBits<4>(data, 1, 0b0001)) {
            set = std::make_shared<SET>(dspInstInfo->getAddress(), baseCycle);
            set->isCompactInstruction = true;
            set->compactInstKind = "Sc5";
            set->cst = extractBits<3>(data, 13).to_ulong();
            set->cst += (extractBits<2>(data, 11).to_ulong()) << 3;
            set->auxCst = set->cst;
            set->dst = set->src2 = extractBits<3>(data, 7).to_ulong();
            set->s = data[0];
            if (set->s == 0) {
                set->funcUnit = FuncUnit::S1;
            } else {
                set->funcUnit = FuncUnit::S2;
            }
            set->DSZ = dspInstInfo->getDSZ();
            set->RS = dspInstInfo->getRS();
            set->SAT = dspInstInfo->getSAT();
            set->PROT = dspInstInfo->getPROT();
            set->BR = dspInstInfo->getBR();
            set->p = dspInstInfo->getP();
            set->fixUpRegOnCompactInstruction();
            set->instFormat = InstFormat::Kind1;
        }
    }
    return set;
}

void SET::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {
    u32 begin = 0;
    u32 end = 0;
    std::bitset<32> src2;
    switch (instFormat) {
        case InstFormat::Kind1:
            src2 = *src2LoRegPtr;
            for (u32 i = 0; i < 32; ++i) {
                if (i >= cst && i <= auxCst) {
                    src2[i] = true;
                }
            }
            *dstLoRegPtr = src2.to_ulong();
            break;
        case InstFormat::Kind2:
            begin = *src1LoRegPtr & 0x000003E0;
            end = *src1LoRegPtr & 0x1f;
            src2 = *src2LoRegPtr;
            for (u32 i = 0; i < 32; ++i) {
                if (i >= begin && i <= end) {
                    src2[i] = true;
                }
            }
            *dstLoRegPtr = src2.to_ulong();
            break;
        default:
            break;
    }
}

std::string SET::toString() const {
    std::string res;
    if (!isCompactInstruction) {
        res += getStandardConditionString() + " ";
    }
    res += m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " ";
    if (instFormat == InstFormat::Kind1) {
        res += getSrc2Reg32Name() + "," + std::to_string(cst) + "," + std::to_string(auxCst) + "," + getDstReg32Name();
    } else if (instFormat == InstFormat::Kind2) {
        res += getSrc2Reg32Name() + "," + getSrc1Reg32Name() + "," + getDstReg32Name();
    }
    return res;
}

std::shared_ptr<DspInstruction> SET::clone() {
    return std::make_shared<SET>(*this);
}
